package com.ruowei.modules.webSocket.web.task;

import cn.jpush.api.push.PushResult;
import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQueryFactory;

import com.ruowei.modules.webSocket.domain.Message;
import com.ruowei.modules.webSocket.domain.MessagePush;
import com.ruowei.modules.webSocket.domain.QMessage;
import com.ruowei.modules.webSocket.domain.QMessagePush;
import com.ruowei.modules.webSocket.domain.enumeration.ReceiverType;
import com.ruowei.modules.webSocket.repository.MessagePushRepository;
import com.ruowei.modules.webSocket.web.push.JPushUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Component
public class JPushTask {
    private ConcurrentTaskScheduler schedule = new ConcurrentTaskScheduler();

    private final Logger log = LoggerFactory.getLogger(JPushTask.class);
    private final MessagePushRepository messagePushRepository;
    private final JPAQueryFactory jpaQueryFactory;

    /**
     * 是否达到窗口期数量限制
     */
    private static Boolean isCurrentPeriodLimit = false;
    /**
     * 下一窗口期时间
     */
    private static Date nextPeriodStartTime;
    /**
     * 是否有下一执行计划
     */
    private static Boolean isNextTaskPlan = false;

    public JPushTask(MessagePushRepository messagePushRepository, JPAQueryFactory jpaQueryFactory) {
        this.messagePushRepository = messagePushRepository;
        this.jpaQueryFactory = jpaQueryFactory;
    }

    /**
     * @param message
     * @param app
     */
    public void startPushTask(Message message, MessagePush app) {
        //先判断是否达到极光推送的窗口期数量限制
        if (isCurrentPeriodLimit) {
            if (nextPeriodStartTime != null) {
                if (nextPeriodStartTime.before(Date.from(Instant.now()))) {
                    //如果下一窗口期在当前时间之前，说明 窗口期已过
                    isCurrentPeriodLimit = false;
                    //立即执行推送,更新推送时间，如遇异常及时中止
                    pushImmediately(message, app);
                } else {
                    //如果下一窗口期在当前时间之后，确保确实有定时任务
                    if (isNextTaskPlan) {
                        log.info("达到了当前窗口期的数量限制，已有定时计划，下一窗口期的开始时间:" + nextPeriodStartTime);
                    } else {
                        this.schedule.schedule(new Runnable() {
                            @Override
                            public void run() {
                                // 任务逻辑
                                taskPlan();
                            }

                        }, nextPeriodStartTime);
                        log.info("达到了当前窗口期的数量限制，启动一个定时计划，下一窗口期的开始时间:" + nextPeriodStartTime);
                    }

                }
            } else {
                //正常不存在这种情况
                log.error("达到了当前窗口期的数量限制，但不知道下一窗口期的开始时间");
            }
        } else {
            //立即执行推送,更新推送时间，如遇异常及时中止
            pushImmediately(message, app);
        }
        //把消息保存起来
        messagePushRepository.saveAndFlush(app);
    }

    /**
     * 立即推送
     *
     * @param message
     * @param app
     */
    public void pushImmediately(Message message, MessagePush app) {
        //更新推送时间，即使推送失败了也算推送
        app.setPushTime(Instant.now());
        PushResult result = JPushUtil.commonPush(message, app.getReceiverType());
        if (result == null) {
            log.error("推送异常中止");
            app.pushed(false);
        } else {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(Date.from(Instant.now()));
            calendar.add(Calendar.MINUTE, result.getRateLimitReset());
            nextPeriodStartTime = calendar.getTime();

            if (result.getRateLimitRemaining() < 1) {
                //当前窗口没有剩余推送次数了，退出，等下一窗口时间
                app.pushed(false);
                isCurrentPeriodLimit = true;
                log.info("当前窗口剩余请求次数不足1条，已达到数量限制，下一窗口期为：" + nextPeriodStartTime);
            } else {
                app.pushed(true);
                log.info("极光推送成功");
                isCurrentPeriodLimit = false;
            }
        }
    }

    private void taskPlan() {
        log.info("开始执行极光推送的批量任务");
        isCurrentPeriodLimit = false;
        //查找消息计划发送时间在当前时间之前的
        //推送时间为空的
        //接收端为APP的
        // 消息

        QMessage qMessage = QMessage.message;
        QMessagePush qMessagePush = QMessagePush.messagePush;
        List<Tuple> tupleList = jpaQueryFactory.select(qMessage, qMessagePush)
            .from(qMessagePush)
            .leftJoin(qMessage).on(qMessagePush.messageCode.eq(qMessage.messageCode))
            .where(qMessage.planSendTime.lt(Instant.now()))
            .where(qMessagePush.pushTime.isNull())
            .where(qMessagePush.receiverType.in(ReceiverType.CAR_APP, ReceiverType.COURIER_APP, ReceiverType.COURIER_APP))
            .orderBy(qMessage.planSendTime.asc())
            .fetch();
        MessagePush currentPush;
        Message currentMsg;
        List<MessagePush> pushedList = new ArrayList<>();
        log.info("查询需要执行的极光推送消息数量为：" + tupleList.size());
        if (tupleList.size() > 0) {
            for (Tuple tuple : tupleList) {
                currentPush = tuple.get(qMessagePush);
                currentMsg = tuple.get(qMessage);
                if (currentPush != null && currentMsg != null) {
                    PushResult currentResult = JPushUtil.commonPush(currentMsg, currentPush.getReceiverType());
                    //不论极光推送实际是否成功，都看做已发送
                    currentPush.setPushTime(Instant.now());
                    pushedList.add(currentPush);
                    if (currentResult == null) {
                        log.error("推送异常中止");
                        break;
                    } else {
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(Date.from(Instant.now()));
                        calendar.add(Calendar.MINUTE, currentResult.getRateLimitReset());
                        nextPeriodStartTime = calendar.getTime();
                        if (currentResult.getRateLimitRemaining() < 1) {
                            //当前窗口没有剩余推送次数了，退出，等下一窗口时间
                            log.info("当前窗口剩余请求次数不足1条，还剩" + (tupleList.size() - pushedList.size()) + "条消息待发送，下一窗口期为：" + nextPeriodStartTime);
                            isCurrentPeriodLimit = true;
                            break;
                        }
                    }
                }
            }
            if (pushedList.size() > 0) {
                messagePushRepository.saveAll(pushedList);
            }
        }
    }


    public ConcurrentTaskScheduler getSchedule() {
        return schedule;
    }

    public void setSchedule(ConcurrentTaskScheduler schedule) {
        this.schedule = schedule;
    }

    public static Boolean getIsCurrentPeriodLimit() {
        return isCurrentPeriodLimit;
    }

    public static void setIsCurrentPeriodLimit(Boolean isCurrentPeriodLimit) {
        JPushTask.isCurrentPeriodLimit = isCurrentPeriodLimit;
    }

    public static Date getNextPeriodStartTime() {
        return nextPeriodStartTime;
    }

    public static void setNextPeriodStartTime(Date nextPeriodStartTime) {
        JPushTask.nextPeriodStartTime = nextPeriodStartTime;
    }

    public static Boolean getIsNextTaskPlan() {
        return isNextTaskPlan;
    }

    public static void setIsNextTaskPlan(Boolean isNextTaskPlan) {
        JPushTask.isNextTaskPlan = isNextTaskPlan;
    }
}
